#include <iostream>
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <functional>
#include <memory>
#include <unistd.h>

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;

// 定时任务
class TimerTask
{
private:
    uint64_t _id;         // 定時器任务对象
    uint32_t _timeout;    // 定时任务超时时间
    bool _canceled;       // false-表示任务没有被取消，true-表示任务取消
    TaskFunc _task_cb;    // 定时器对象要执行的任务
    ReleaseFunc _release; // 用于删除TimerWheel时间中保存的定时器对象信息

public:
    TimerTask(uint64_t id, uint32_t delay, const TaskFunc &cb)
        : _id(id), _timeout(delay), _task_cb(cb), _canceled(false) {}
    ~TimerTask()
    {
        if (_canceled == false)
            _task_cb(); // 定时任务被取消就不执行任务
        _release();
    }

    void Cancel() { _canceled = true; }                      // 取消定时任务
    void SetRelease(const ReleaseFunc &cb) { _release = cb; } // 设置_release的回调
    uint32_t DelayTime() { return _timeout; }                 // 获取超时时间
};

// 定时时间轮：每一秒数组指针后移一步，数组大小表示最多能处理多少秒后的任务
class TimerWheel
{
private:
    // 使用智能指针指向定时任务，引用计数为零，直接释放对象，
    using PtrTask = std::shared_ptr<TimerTask>;     // 将定时任务添加到时间轮中，使用shared_ptr增加引用计数，延长定时任务
    using WeakTask = std::weak_ptr<TimerTask>;      // 将定时任务添加到哈希表中，使用weak_ptr不增加引用计数，否则定时任务无法被释放
    int _tick;                                      // 当前的秒针，走到哪里释放哪里，释放哪里，就相当于执行哪里的任务
    int _capacity;                                  // 时间轮最大容量---其实就是最大延迟时间
    std::vector<std::vector<PtrTask>> _wheel;       // 时间轮数组
    std::unordered_map<uint64_t, WeakTask> _timers; // 存储定时任务信息的哈希表<id,weak_ptr<TimerTask>>，一一对应，这样才能在不影响计数器的同时，获取shared_ptr
private:
    void RemoveTimer(uint64_t id)
    {
        // 删除定时任务信息
        auto it = _timers.find(id);
        if (it != _timers.end())
        {
            _timers.erase(it);
        }
    }

public:
    TimerWheel() : _capacity(60), _tick(0), _wheel(_capacity) {}

    // 向时间轮中添加定时任务
    void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb)
    {
        // 60s以上和0s以下的定时任务不⽀持
        if (delay > _capacity || delay <= 0)
            return;
        PtrTask pt(new TimerTask(id, delay, cb));
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id)); // 绑定this指针与_id，每个定时任务对象设置自己的release回调
        int pos = (_tick + delay) % _capacity;                         // 查找定时任务在时间轮中的位置
        _wheel[pos].push_back(pt);                                     // 插入到对应位置的任务数组
        _timers[id] = WeakTask(pt);                                    // 将任务信息存到哈希表中
    }

    // 刷新/延迟定时任务
    void TimerRefresh(uint64_t id)
    {
        // 通过保存的定时器对象的weak_ptr构造一个shared_ptr出来，添加到时间轮中
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return; // 没找着定时任务，没法刷新，没法延迟
        }
        PtrTask pt = it->second.lock(); // lock获取weak_ptr管理的对象对应的shared_ptr
        int delay = pt->DelayTime();
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
    }

    // 取消定时任务
    void TimerCancel(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return; // 没找着定时任务，没法刷新，没法延迟
        }
        PtrTask pt = it->second.lock();
        if (pt)
            pt->Cancel();
    }

    // 这个函数应该每秒钟被执行一次，相当于秒针向后走了一步
    void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear(); // 清空指定位置的数组，就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
    }
};

class Test
{
public:
    Test() { std::cout << "构造" << std::endl; }
    ~Test() { std::cout << "析构" << std::endl; }
};

void DelTest(Test *t)
{
    delete t;
}

int main()
{
    TimerWheel tw;

    Test *t = new Test();

    tw.TimerAdd(888, 5, std::bind(DelTest, t));

    for (int i = 0; i < 5; i++)
    {
        sleep(1);
        tw.TimerRefresh(888); // 刷新定时任务
        tw.RunTimerTask();    // 向后移动秒针
        std::cout << "刷新了一下定时任务，重新需要5s中后才会销毁\n";
    }
    tw.TimerCancel(888);
    while (1)
    {
        sleep(1);
        std::cout << "-------------------\n";
        tw.RunTimerTask(); // 向后移动秒针
    }
    return 0;
}